<?php
 
/**
 * DefaultDateTimeParser converts a date/time string to an array
 *
 * The following pattern characters are recognized:
 * <pre>
 * Pattern |      Description
 * ----------------------------------------------------
 * d       | Day of month 1 to 31, no padding
 * dd      | Day of month 01 to 31, zero leading
 * M       | Month digit 1 to 12, no padding
 * MM      | Month digit 01 to 12, zero leading
 * yy      | 2 year digit, e.g., 96, 05
 * yyyy    | 4 year digit, e.g., 2005
 * h       | Hour in 0 to 23, no padding
 * hh      | Hour in 00 to 23, zero leading
 * H       | Hour in 0 to 23, no padding
 * HH      | Hour in 00 to 23, zero leading
 * m       | Minutes in 0 to 59, no padding
 * mm      | Minutes in 00 to 59, zero leading
 * s       | Seconds in 0 to 59, no padding
 * ss      | Seconds in 00 to 59, zero leading
 * a       | AM or PM, case-insensitive (since version 1.1.5)
 * ----------------------------------------------------
 * </pre>
 *
 *
 * Modified version of http://www.yiiframework.com/doc/api/1.1/CDateTimeParser
 *
 * This version will accept a pattern and default the time values for any missing pattern
 * It returns a string rather than a timestamp in case its the wrong timezone
 * Also uses the LocalTime class to get the time for now() in the users timezone
 * For example, DefaultDateTimeParser::parse('31/12/2011','dd/MM/yyyy',array('hour'=>0,'minute'=>0,'day'=>0);
 * Will return '2011-12-2011 0:0:0'
 */
class DefaultDateTimeParser
{
 
    public static function parse($value,$pattern='MM/dd/yyyy',$defaults=array())
    {
        $tokens=self::tokenize($pattern);
        $i=0;
        $n=strlen($value);
        foreach($tokens as $token)
        {
            switch($token)
            {
                case 'yyyy':
                {
                    if(($year=self::parseInteger($value,$i,4,4))!==null)
                        $i+=4;
                    break;
                }
                case 'yy':
                {
                    if(($year=self::parseInteger($value,$i,1,2))!==null)
                        $i+=strlen($year);
                    break;
                }
                case 'MM':
                {
                    if(($month=self::parseInteger($value,$i,2,2))!==null)
                        $i+=2;
                    break;
                }
                case 'M':
                {
                    if(($month=self::parseInteger($value,$i,1,2))!==null)
                        $i+=strlen($month);
                    break;
                }
                case 'dd':
                {
                    if(($day=self::parseInteger($value,$i,2,2))!==null)
                        $i+=2;
                    break;
                }
                case 'd':
                {
                    if(($day=self::parseInteger($value,$i,1,2))!==null)
                        $i+=strlen($day);
                    break;
                }
                case 'h':
                case 'H':
                {
                    if(($hour=self::parseInteger($value,$i,1,2))!==null)
                        $i+=strlen($hour);
                    break;
                }
                case 'hh':
                case 'HH':
                {
                    if(($hour=self::parseInteger($value,$i,2,2))!==null)
                        $i+=2;
                    break;
                }
                case 'm':
                {
                    if(($minute=self::parseInteger($value,$i,1,2))!==null)
                        $i+=strlen($minute);
                    break;
                }
                case 'mm':
                {
                    if(($minute=self::parseInteger($value,$i,2,2))!==null)
                        $i+=2;
                    break;
                }
                case 's':
                {
                    if(($second=self::parseInteger($value,$i,1,2))!==null)
                        $i+=strlen($second);
                    break;
                }
                case 'ss':
                {
                    if(($second=self::parseInteger($value,$i,2,2))!==null)
                        $i+=2;
                    break;
                }
                case 'a':
                {
                    // If this value isn't present then ignore it
                    if(($ampm=self::parseAmPm($value,$i))===null)
                        break;
 
                    if(isset($hour))
                    {
                        if($hour==12 && $ampm==='am')
                            $hour=0;
                        else if($hour<12 && $ampm==='pm')
                            $hour+=12;
                    }
                    $i+=2;
                    break;
                }
                default:
                {
                    // If the separator pattern doesn't exist in the value, then ignore it
                    // eg: a space
                    if (strpos($value, $token)===false)
                            break;
 
                    $tn=strlen($token);
                    if($i>=$n || substr($value,$i,$tn)!==$token)
                        return false;
                    $i+=$tn;
                    break;
                }
            }
        }
        if($i<$n) // somethings gone wrong
            return false;
 
        // Defaults to the date/time for the local timezone
        // If you don't want to use Yii::app()-localtime->localNow() then simply replace with the php date() function
        // Yii::app()->localtime-> = LocalTime::
        if(!isset($year))
            $year=isset($defaults['year']) ? $defaults['year'] : Yii::app()->localtime->localNow('Y'); // date('Y');
        if(!isset($month))
            $month=isset($defaults['month']) ? $defaults['month'] : Yii::app()->localtime->localNow('n'); // date('n');
        if(!isset($day))
            $day=isset($defaults['day']) ? $defaults['day'] : Yii::app()->localtime->localNow('j'); // date('j');
        if(!isset($hour))
            $hour=isset($defaults['hour']) ? $defaults['hour'] : Yii::app()->localtime->localNow('H'); // date('H');
        if(!isset($minute))
            $minute=isset($defaults['minute']) ? $defaults['minute'] : Yii::app()->localtime->localNow('i'); // date('i');
        if(!isset($second))
            $second=isset($defaults['second']) ? $defaults['second'] : Yii::app()->localtime->localNow('s'); // date('s');
 
        $year=(int)$year;
        $month=(int)$month;
        $day=(int)$day;
        $hour=(int)$hour;
        $minute=(int)$minute;
        $second=(int)$second;
 
 
        if(CTimestamp::isValidDate($year,$month,$day) && CTimestamp::isValidTime($hour,$minute,$second))
        {
            // Return a time string rather than a timestamp because the timestamp might be the wrong timezone?
            return $year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':'.$second;
        }
        else
            return false;
    }
 
    /*
     * @param string $pattern the pattern that the date string is following
     */
    private static function tokenize($pattern)
    {
        if(!($n=strlen($pattern)))
            return array();
        $tokens=array();
        for($c0=$pattern[0],$start=0,$i=1;$i<$n;++$i)
        {
            if(($c=$pattern[$i])!==$c0)
            {
                $tokens[]=substr($pattern,$start,$i-$start);
                $c0=$c;
                $start=$i;
            }
        }
        $tokens[]=substr($pattern,$start,$n-$start);
        return $tokens;
    }
 
    /*
     * @param string $value the date string to be parsed
     * @param integer $offset starting offset
     * @param integer $minLength minimum length
     * @param integer $maxLength maximum length
     */
    protected static function parseInteger($value,$offset,$minLength,$maxLength)
    {
        for($len=$maxLength;$len>=$minLength;--$len)
        {
            $v=substr($value,$offset,$len);
            if(ctype_digit($v) && strlen($v)>=$minLength)
                return $v;
        }
        // Changed by Russell England to null rather than false
        return null;
    }
 
    /*
     * @param string $value the date string to be parsed
     * @param integer $offset starting offset
     */
    protected static function parseAmPm($value, $offset)
    {
        $v=strtolower(substr($value,$offset,2));
        return $v==='am' || $v==='pm' ? $v : false;
    }
}